package system.train;

import system.communication.Monitor;
import system.track.Block;
import system.track.Direction;
import system.track.SwitchAngle;

/**
 * This computer-controlled train has dynamic pathing abilities.
 * @author David Kolb & Yasmine Kandissounon.
 */
public class SmartTrain extends Train
{
    public SmartTrain(Block block, int id)
    {
        super(block, id);
    }

   @Override
   public void setRoute(Direction direction)
   {
       
   }

   @Override
   public void run()
   {
       initialActions();
       while(isRunning())
       {
           preDecisionActions();
           makeDecisions();
           postDecisionActions();
       }
   }

   private void makeDecisions()
   {
       boolean reserveAttempt;
       if(canSetNextSwitch())
       {
           reserveAttempt = this.getCurrentBlock()
                   .getSpecificBlock(this.getDirection(),
                   SwitchAngle.ONE_EIGHTY_DEGREES).reserve();
           if(reserveAttempt)
           {
               RouteActions.SET_NEXT_SWITCH_180.executeAction(this);
           }
           else
           {
               reserveAttempt = this.getCurrentBlock()
                   .getSpecificBlock(this.getDirection(),
                   SwitchAngle.NINETY_DEGREES).reserve();
               if(reserveAttempt)
               {
                   RouteActions.SET_NEXT_SWITCH_90.executeAction(this);
               }
               else
               {
                  attemptPreviousBlocks();
               }
           }
       }
       else
       {
           reserveAttempt = reserveBlock(this.getDirection(),
                   null);
           if(reserveAttempt)
           {
              this.getCurrentBlock().autoSetSwitch(this.getDirection());
           }
           else
           {
               attemptPreviousBlocks();
           }
       }
   }

   private void attemptPreviousBlocks()
   {
       boolean reserveAttempt;
       if (canSetPreviousSwitch())
       {
           reserveAttempt = this.getCurrentBlock().getSpecificBlock(this.getReversedDirection(),
                   SwitchAngle.ONE_EIGHTY_DEGREES).reserve();
           if (reserveAttempt)
           {
               RouteActions.TOGGLE_DIRECTION.executeAction(this);
               RouteActions.SET_NEXT_SWITCH_180.executeAction(this);
           }
           else
           {
               reserveAttempt = reserveBlock(getReversedDirection(),
                       SwitchAngle.NINETY_DEGREES);
               if (reserveAttempt)
               {
                   RouteActions.TOGGLE_DIRECTION.executeAction(this);
                   RouteActions.SET_NEXT_SWITCH_90.executeAction(this);
               }
               else
               {
                   errorStopAll();
               }
           }
       }
       else
       {
           reserveAttempt = reserveBlock(getReversedDirection(),
                   null);
           if (reserveAttempt)
           {
               RouteActions.TOGGLE_DIRECTION.executeAction(this);
               this.getCurrentBlock().autoSetSwitch(this.getDirection());
           }
           else
           {
               errorStopAll();
           }
       }
   }

   private boolean canSetNextSwitch()
   {
       return this.getCurrentBlock().canSetSwitch(this.getDirection());
   }

   private boolean canSetPreviousSwitch()
   {
       return this.getCurrentBlock().canSetSwitch(getReversedDirection());
   }

   private boolean reserveBlock(Direction d, SwitchAngle a)
   {
       return this.getCurrentBlock().getSpecificBlock(d,a).reserve();
   }
   
   private void initialActions()
   {
       RouteActions.SET_NEXT_SWITCH_180.executeAction(this);
       this.getCurrentBlock().getNextBlock(this.getDirection())
               .reserve();
       RouteActions.ACTIVATE_NEXT_BLOCK.executeAction(this);
       RouteActions.REGISTER_SENSORS.executeAction(this);
       RouteActions.ACTIVATE_CURRENT_BLOCK.executeAction(this);
   }

   private void preDecisionActions()
   {
      waiting = true;
      waitForSensor();
      RouteActions.UNREGISTER_SENSORS.executeAction(this);
      RouteActions.DEACTIVATE_PREVIOUS_BLOCK.executeAction(this);
      this.getCurrentBlock().getNextBlock(this.getDirection())
               .unreserve();
      RouteActions.MOVE_TRAIN_AHEAD.executeAction(this);
   }

   private void postDecisionActions()
   {
       RouteActions.REGISTER_SENSORS.executeAction(this);
       RouteActions.ACTIVATE_NEXT_BLOCK.executeAction(this);
   }

   private void errorStopAll()
   {
      Monitor.getInstance().stop();
      throw new IllegalTrackStateException("can't make a decision");
   }

   private Direction getReversedDirection()
   {
       Direction returnVal = Direction.CLOCKWISE;
       if (getDirection() == returnVal)
       {
           returnVal =Direction.COUNTERCLOCKWISE;
       }
       return returnVal;
   }
}


